package no.sogl.domain;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.tostring.RooToString;
import org.springframework.transaction.annotation.Transactional;

@RooJavaBean
@RooToString
@RooJpaActiveRecord(finders = { "findTeamsByNameEquals" })
public class Team {

    @Column(unique = true)
    @Size(min = 5, max = 50)
    private String name;

    @NotNull
    @Min(3)
    @Max(70)
    private int fromAge = 3;

    @NotNull
    @Min(3)
    @Max(70)
    private int toAge = 18;

    @NotNull
    private int trainingFee = 500;

    @NotNull
    private boolean active = true;

    @Size(max = 255)
    private String description;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "team")
    private Set<TeamCoach> coaches = new HashSet<TeamCoach>();

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "team")
    private Set<TeamContact> contacts = new HashSet<TeamContact>();

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "team")
    private Set<TeamMember> members = new HashSet<TeamMember>();

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "S-")
    @Column(updatable = false, insertable = true)
    private Date createdAt = new Date();

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "SS")
    private Date updatedAt = new Date();

    public Team() {
        super();
    }

    public Team(String name, int fromAge, int toAge) {
        this();
        this.name = name;
        this.fromAge = fromAge;
        this.toAge = toAge;
    }

    @Transactional
    public void persist() {
        EntityManager em = Team.entityManager();
        em.persist(this);
        createTag();
    }

    @Transactional
    public no.sogl.domain.Team merge() {
        updatedAt = new Date();
        EntityManager em = Team.entityManager();
        Team merged = em.merge(this);
        if (!active) removeTag();
        else createTag();
        em.flush();
        return merged;
    }

    @Transactional
    public void remove() {
        EntityManager em = Team.entityManager();
        if (em.contains(this)) {
            em.remove(this);
        } else {
            Team attached = Team.findTeam(getId());
            em.remove(attached);
        }
        removeTag();
    }

    private void createTag() {
        Tag tag = Tag.findTagByNameEquals(this.name);
        if (tag != null) return;
        tag = new Tag();
        tag.setName(this.name);
        tag.persist();
    }

    private void removeTag() {
        Tag tag = Tag.findTagByNameEquals(this.name);
        if (tag != null) tag.remove();
    }

    public static Team findTeamByNameEquals(String name) {
        if (name == null || name.length() == 0) throw new IllegalArgumentException("The name argument is required");
        EntityManager em = Team.entityManager();
        TypedQuery<Team> q = em.createQuery("SELECT o FROM Team AS o WHERE o.name = :name", Team.class);
        q.setParameter("name", name);
        try {
            return q.getSingleResult();
        } catch (EmptyResultDataAccessException e) {
            return null;
        }
    }
    public static Team findTeamByAge(int age) {
        EntityManager em = Team.entityManager();
        TypedQuery<Team> q = em.createQuery("SELECT o FROM Team AS o WHERE :age between o.fromAge and o.toAge", Team.class);
        q.setParameter("age", age);
        try {
            return q.getSingleResult();
        } catch (EmptyResultDataAccessException e) {
            return null;
        }
    }
}
